#include "interp.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

Table_ Table(string id, int value, Table_ tail) {
	Table_ t = checked_malloc(sizeof *t);
	t->id = id;
	t->value = value;
	t->tail = tail;
}

Table_ update(Table_ t1, string id, int value) {
	// put the new node behind the head
	return Table(id, value, t1);
}

int lookup(Table_ t1, string id) {
	while(t1) {
		if(!strcmp(t1->id, id))
			return t1->value;	
	}
	fprintf(stderr, "Not Found %s!\n", id);
	exit(1);
}

Table_ interpStm(A_stm stm, Table_ t) {
	// if 'stm' is CompoundStm, then interpStm(..) twice
  	// if 'stm' is AssignStm, then interpExp(stm.exp) and update()
	// if 'stm' is PrintStm, then for each exp in stm->explist
	// interpExp(exp) and print the result of exp

	struct IntAndTable iat;
	A_expList p;

	if(stm->kind == A_compoundStm) {
		t = interpStm(stm->u.compound.stm1, t);
		return interpStm(stm->u.compound.stm2, t);			
	}
	else if(stm->kind == A_assignStm) {
		iat = interpExp(stm->u.assign.exp, t);
		// 'iat.t' record the latest table head pointer!
		return update(iat.t, stm->u.assign.id, iat.i);
	}
	else if(stm->kind == A_printStm){
		p = stm->u.print.exps;
		while(p->kind == A_pairExpList) {
			iat = interpExp(p->u.pair.head, t);
			// print the result of expression
			printf("%d ", iat.i);
			// update 't' for following interpExp()
			t = iat.t; 
			p = p->u.pair.tail;
		}
		// solve the lastExpList
		iat = interpExp(p->u.last, t);
		printf("%d\n", iat.i);
		return iat.t;
	}
	else {
		fprintf(stderr, "Unknown kind statement!\n");
		exit(1);	
	}
}

struct IntAndTable interpExp(A_exp exp, Table_ t) {
	// if exp is idExp, then lookup the table
	// if exp is numExp, then return the value of number
	// if exp is op_Exp, then interpExp() twice and compute the result
	// if exp is eseq_Exp, then interpStm() and interpExp()

	struct IntAndTable iat;

	if(exp->kind == A_idExp) {
    	iat.i = lookup(t, exp->u.id);
		iat.t = t;
		return iat;
	}
	else if(exp->kind == A_numExp) {
		iat.i = exp->u.num;
		iat.t = t;
		return iat;
	}
	else if(exp->kind == A_opExp) {
		int res1, res2;
		iat = interpExp(exp->u.op.left, t);
		res1 = iat.i;
		iat = interpExp(exp->u.op.right, iat.t);
		res2 = iat.i;
		switch(exp->u.op.oper) {
			case A_plus: {
				iat.i = res1 + res2;
				break;
			} 
			case A_minus: {
				iat.i = res1 - res2;
				break;			
			}
			case A_times: {
				iat.i = res1 * res2;
				break;			
			}
			case A_div:	{
				iat.i = res1 / res2;
				break;			
			}
			default: {
				fprintf(stderr, "Error binary operator!\n");
				exit(1);			
			}
		}	
		return iat;		
	}
	else if(exp->kind == A_eseqExp){	
		t = interpStm(exp->u.eseq.stm, t);
		return interpExp(exp->u.eseq.exp, t);
	}
	else {
		fprintf(stderr, "Unknown kind expression!\n");
		exit(1);
	}
}
